March 26, 2021
์ง๋์ฃผ ๊ธ์์ผ์ ์น ๊ฐ๋ฐ ๋ถํธ์บ ํ ๊ณผ์ ์ ์๋ฃํ๋ค.
๊ทธ๋ ๊ฒ ์ฃผ๋ง์ ๋ณด๋ด๊ณ ์์์ผ์ด ๋์๊ณ ์ง๊ธ์ ๊ธ์์ผ ์ ๋ ์ด๋ค.
ํ์ด๋ ํ๋ก์ ํธ๋ฅผ ์กฐ๊ธ ๋ ๋ฉ์ง๊ฒ ๊ฐ๊พธ์ด ๋๊ฐ๋ ์ค์ด๋ค. ๋ฌผ๋ก ์๋ํ ๋ฐ๋๋ก ๊ธฐ๋ฅ์ ๊ตฌํํ๋๋ฐ ์กฐ๊ธ ๋ ๋ ธ๋ ฅ์ ๋ค์ฌ์ผ ํ๋ค.
ํ์๋ค๊ณผ ํ๋ก์ ํธ๋ฅผ ๋ค๋ฌ์ ๊ฒฝํ์ ์ด์ผ๊ธฐ ํ๊ธฐ ์ ์ ์ ํ๊ณผ ์ง์ค์ ๋ ์ํด์ผ ๊ฒ ๋ค๋ ์๊ฐ์ด ๋ ๋ค.
์ฝ๋ฉ ํ ์คํธ๋ ์ฐ์ตํด์ผ ํ๊ณ , ๊ธฐ์ด์ ์ธ CS ์ง์์ ๋ํ ํ์ต๊ณผ ๊ทธ๋์ ์ด ๋ธ๋ก๊ทธ ๊ธ์ ํตํ ๋ณต์ต๋ ํ์ํ๋ค.
์์ฌ์ด ๋ง์์ ๋ง ์ฌ๊ธฐ ์ ๊ธฐ ํผ์ณ ๋๊ณ ํ ๋ฒ์ ๋ค ๋จน์ผ๋ ค๊ณ ํ๋ ์ ์ ๋ฐ์ฑํ๋ค.
๋ถํธ์บ ํ์์ ๊ฐ์๊ฐ ๋ชจ๋ ๊ฐ๋ฐ์๊ฐ ๋๋ ค๋ ์ฌ์ฐ๋ค์ด ์์๋ค. ๋๋ ๋ง์ฐฌ๊ฐ์ง๋ค. ๋ ํด๋ผ ๊ฑฐ๊ณ ๋ฟ๋ฆฌ๋ฅผ ๋ด๋ ค์ ๋ค์ ๊ฝํผ์ ๋๊ฐ ๊ฒ์ด๋ค.
gif ๋ฅผ ํตํด ์๋น์ค๋ฅผ ์์ฐํ๋ ๋ชจ์ต๊ณผ ๋๋ถ์ด ์์ ์ค๋ช ๊ธ์ด ์๋๋ฐ, ํ์๋ค์ด ์ข์ ์๊ฒฌ์ ๋ด์ฃผ์ ์ ํฐํธ์ ํฌ๊ธฐ๋ฅผ ์ข๋ ๋์ ๋ค์ด์ฌ ์ ์๋๋ก ๋ณ๊ฒฝํด ์ฃผ์๋ค.
๋ํ footer ๋ถ๋ถ์ด ๋๋ฌด ์ผ์ชฝ์ผ๋ก ์น์ฐ์ณ์ง ๋ฏ ํ์ฌ ๊ทธ ๋ถ๋ถ๋ ๊ฐ์ด๋ฐ๋ก ์ ๋ ฌ์ด ๋๋๋ก ์์ ํ์๋ค. ๊ทธ๋ฆฌ๊ณ ๋ก๊ณ ์ Github ๋งํฌ๋ฅผ ํด๋ฆญํ๋ฉด ๊ฐ๊ฐ ํ์ด๋ ํ๋ก์ ํธ ๊ธฐํ์ (๋ ธ์ ) ์ Github Repo ์ Wiki ํ์ด์ง๋ก ์ด๋ํ ์ ์๊ฒ ํด ์ฃผ์๋ค.
ํ์๋ถ๋ค์ด ์กฐ๊ธ ๋ ์ ๋ฌธ์ ์ธ ๋๋์ ์ฃผ๊ธฐ ์ํด์๋ ์ ์ฒด์ ์ผ๋ก ๊ฝ ์ฐฌ UI ์ ๋๋๋ณด๋ค๋, ๋ค์ด๋ฒ ํฌํธ ํ๋ฉด์ฒ๋ผ ์ข์ฐ ์ฌ๋ฐฑ์ด ์๊ฒ UI ๋ฅผ ๊ฐ์ ํด ๋ณด๋ฉด ์ด๋จ๊น ์ ์์ ํด์ฃผ์ ์ ๊ทธ ์ ๋ ๋ฐ์ํ๋ค.
์ด ๋ถ๋ถ์ ๋ค๋ฅธ ํ์ด์ง์๋ ๋์ผํ๊ฒ ์ฌ๋ฐฑ์ ์ฃผ์ด ์ ์ฉํ๋ค.
์ด์ ์๋ ์ด๋ชจ์ง์ ๋จ๋ฐ์ด ์ข ์์๋ ๊ฑฐ ๊ฐ์ ์ต์ํ์ ์ด๋ชจ์ง๋ฅผ ์ ์ธํ๊ณ ๋ ๋ชจ๋ ์ง์ ๋ค.
๋จผ์ ์์ ์ ์ ๋ง์ดํ์ด์ง์ ์ฌ์ฉ์ ์ ๋ณด UI ๋ฅผ ๋ณด์.
alt ์์ฑ์ด๋ ๊นจ์ง ๋ฏํด ๋ณด์ด๋ ์ฌ์ฉ์ ์๋ฐํ ๋ฐ์ค๊ฐ ๋ฌด์ธ๊ฐ ๋ฏธ์์ฑ์ ๋๋์ ํ๊ธฐ๋ ๊ฒ ๊ฐ์๋ค.
๊ทธ๋์ ์ต์ด ํ์๊ฐ์ ํ ์ฌ์ฉ์์ ๊ฒ์คํธ ๋ก๊ทธ์ธ์ ํ ์ฌ์ฉ์์ ๊ธฐ๋ณธ ์๋ฐํ ์ด๋ฏธ์ง๋ฅผ ์ ์ฉํด ์ฃผ์๊ณ ๋ฐฑ๊ทธ๋ผ์ด๋ ์์์ ๋คํ์์ผ๋ก ์ค ๊ฒ์ ์ง์ฐ๊ณ ํด๋น ๊ธฐ๋ณธ ์๋ฐํ์ ๋น์ทํ ์์์ ์ ์ฉํด ์ฃผ์๋ค.
์ด๋ฏธ์ง ์ ํ๊ณผ ์ด๋ฏธ์ง ๋ณ๊ฒฝ ๋ฒํผ์ ์์น๋ ์์ ํด ์ฃผ์๋ค. ์ฌ์ฉ์ ์ ๋ณด์ ๋ํด์๋ ์กฐ๊ธ ๋ ์ค๋ฅธ์ชฝ์ผ๋ก ์ ๋ ฌ์ ํด์ ํต์ผ์ฑ ์๊ฒ ๋ณด์ด๊ฒ ํ์๋ค.
๊ธฐ๋ณธ ๋ฐฐ๊ฒฝ๋ ๊ธฐ์กด์ ํ์ ๋ณด๋ค๋ ํ๊ฐ์ด ๋ณด์ด๋ ๋ทฐ๋ฅผ ๊ฐ์ง ์ด๋ฏธ์ง๋ก ์ ์ฉํด์ฃผ๋ ๋๋์ด ํจ์ฌ ์ด์๋๋ ๊ฒ ๊ฐ๋ค.
๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์์ ํ๋๋ก ํ๋ ๋ชจ๋ฌ์ฐฝ์ UI ๋ ๊ธฐ์กด์ ์์ด๋ฅผ ๋๋ฌด ๋จ๋ฐํ๊ณ ๋์์ธ์ด ๋ณ๋ก์ธ ๋ฏ ํ์ฌ ์ด ๋ถ๋ถ๋ ์์ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ์๋๋ฅผ ๋ด๋ฆฌ๋ฉด ์ค์ผ์ค ์นด๋๊ฐ ์๋๋ฐ ๊ธฐ์กด ํ ํ๋ฉด์ ๊ฝ์ฐฌ ๋๋์์ ๋๋ฉํ์ด์ง ์ฒ๋ผ ์ข์ฐ ์ฌ๋ฐฑ์ ์ถฉ๋ถํ ์ฃผ์๋ค.
์์ gif ๊ฐ ์์์ผ์ ์์ ํ ๋ถ๋ถ์ด์๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก ์ข์ฐ ์ฌ๋ฐฑ์ ์ฃผ์๊ณ ์์ ํ์ ์ง๋ผ๋ ๋ฐ์ํ์๋ ์ ํ ์ํฅ์ ์ฃผ์ง ์๊ฒ ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ๋ค. ํ์ง๋ง ์ฌ๊ธฐ์์ ๋ ๋์๊ฐ ๋ถ๋ถ์ ์๊ฐํ๋ ค๊ณ ํ๋ค.
์ค์ผ์ค๋ฌ ํ์ด์ง๋ฅผ ๊ฐ์ ํ๋ฉด์ ์ฌ๋ฏธ๋ฅผ ๋ง์ด ๋๊ผ์ง๋ง ์์ผ๋ก ์งํํ ๋ถ๋ถ์์ ๋งํ ๋ถ๋ถ๋ ์์ด์ ๊ฐ์ด ํ์ด๋ณด๊ฒ ๋ค.
์๋๋ ์ค์ผ์ค๋ฌ ๋ฉ์ธ ๋ถ๋ถ ์ข์ธก ์ ์๋ Edit ๋ฒํผ์ ๋๋ฌ ๋ชจ๋ฌ์ฐฝ์ ์ด๊ณ ๊ฑฐ๊ธฐ์ ์ค์ผ์ค๋ฌ์ ๋ด์ฉ์ ๋ฃ๊ณ ์์ ํ๋ ค ํ๋ค.
ํ์ง๋ง ์ด๋ ๊ฒ ํ๋ฉด ์๋น์ค๋ฅผ ์ฌ์ฉํ๋ ์ ์ ์ ์ฅ์์ ๋๋ฌด ๋ง์ด ๊ฑฐ์น๊ฒ ๋๊ณ ๋ฒํผ์ ๋ง์ด ๋๋ฅด๊ฒ ๋ง๋๋ ๋จ์ ์ด ์์๋ค.
์ด๋ฌํ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ณ ๋ คํด์๋ผ๋ ์ค์ผ์ค๋ฌ์ ์ ๋ชฉ ๋ถ๋ถ์ ํด๋ฆญํ๋ฉด ์์ ์ ํ ์ ์๋๋ก ํ๋ฉฐ, ์ ๋ชฉ ๋ฐ๊นฅ ๋ถ๋ถ์ ํด๋ฆญํ๋ฉด ๋ค์ ํธ์ง๊ธฐ๋ฅ์ด disabled ๋๊ฒ๋ ํ๋ ๋ฐฉ๋ฒ์ ์๊ฐํด์ผ ํ๋ค.
ํ์๋ถ์ด ๋ณด๋ด์ฃผ์ ๋ธ๋ก๊ทธ ๋ด์ฉ๊ณผ ๋ด๊ฐ ์ฐพ์๋ณธ ๋ด์ฉ๋ค์ ์ทจํฉํด์ ์๋กญ๊ฒ ๋ก์ง์ ๋ค์ ์์ฑํ๊ณ ํ์๋ค์๊ฒ ์ฑ๊ณตํ์์ ์๋ฆฌ๋ gif ๋ฅผ ๋ณด๋๋ค!
import React, { useState, useEffect, useRef } from 'react'
import ModifyThumbs from './popupModal/ModifyThumbs'
const SchedulerMain = () => {
const initial = `๋ฏธ์๋ญ 2์คํ๐๐ ์ ์๋น์์ ์ด์ํ๋ ์ ์๋ฐ, ์ฐ์ธ๊ณผ ํน์ ์น๊ตฌ์ ๋ถ์๊ธฐ๋ฅผ ๋ด๋ฉด ์ข์ ๊ณณ์ด๋ค! ํ์ง๋ง ํผ์ ๊ฐ๋ ๊ทธ ๋ถ์๊ธฐ๋ฅผ 120% ๋๋ ์ ์๋ค!๐จ๐จ ์น์ฆ์๐ง๐ง ๋ ๋์์ธ๐ท๐ท ๊ฐํ๐ ๋ฉด ๋ชจ๋ ๊ฒ ๋๋๋ค!`
const ref = useRef(null)
const [likeCount, setLikeCount] = useState(0)
const [dislikeCount, setDislikeCount] = useState(0)
const [isBookmarked, setIsBookmarked] = useState(false)
const increaseLikeCount = () => {
setLikeCount(likeCount + 1)
}
const increaseDislikeCount = () => {
setDislikeCount(dislikeCount + 1)
}
const handleBookmark = () => {
setIsBookmarked(!isBookmarked)
}
const thumbnailRef = useRef(null)
const [text, setText] = useState(initial)
const [editable, setEditable] = useState(false)
const editOn = () => {
setEditable(true)
}
const handleChange = e => {
setText(e.target.value)
}
const handleKeyDown = e => {
if (e.key === 'Enter') {
setEditable(!editable)
}
}
const handleRemoveTitle = () => {
setText('')
}
const handleClickOutside = e => {
if (editable === true && !ref.current.contains(e.target)) {
setEditable(false)
}
}
useEffect(() => {
window.addEventListener('click', handleClickOutside, true)
})
return (
<section className="schedule-banner">
<div className="schedule-info">
<div className="schedule-avatar">
<img src={'/images/user.png'} alt="avatar" />
</div>
<div ref={ref} className="schedule-title">
{editable ? (
<textarea
type="text"
value={text}
onChange={e => handleChange(e)}
onKeyDown={handleKeyDown}
rows="3"
placeholder="์ค์ผ์ค์ ๋ง๋ ์ ๋ชฉ์ ์
๋ ฅํด ๋ณด์ธ์!"
/>
) : (
<h2 onClick={() => editOn()}>{text}</h2>
)}
<div className="thumbs">
<i className="fas fa-thumbs-up" onClick={increaseLikeCount}>
์ข์์<span>{likeCount}</span>
</i>
<i className="fas fa-thumbs-down" onClick={increaseDislikeCount}>
๋ณ๋ก์์<span>{dislikeCount}</span>
</i>
<i className="fas fa-eraser" onClick={handleRemoveTitle}>
์ง์ฐ๊ฐ
</i>
</div>
</div>
</div>
<ModifyThumbs thumbnailRef={thumbnailRef} />
<div className="shortcut-icons">
<div className="add-thumbnail">
<i
id="open"
className="fas fa-images"
onClick={() => {
if (thumbnailRef.current !== null) {
thumbnailRef.current.classList.toggle('show')
}
}}
></i>
</div>
<div className="bookmark-share">
{!isBookmarked ? (
<i className="far fa-star" onClick={handleBookmark}></i>
) : (
<i
className="fas fa-star"
onClick={handleBookmark}
style={{ color: '#ff514f' }}
></i>
)}
<i className="far fa-share-square"></i>
</div>
</div>
</section>
)
}
export default SchedulerMain
editable ์ด๋ผ๋ ์ํ์ ๋ฐ๋ผ์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ ์คํธ๋ก ๋ณด์ด๊ฒ ๋๋๊ฐ ์๋๋ฉด textarea ๊ฐ ์๋ ๋ฐ์ค๊ฐ ํ์ ๋๋๊ฐ ๋ก ๋๋๊ฒ ๋๋ค.
์ผํญ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ๋ ๋ฏํ ์กฐ๊ฑด๋ถ ๋ ๋๋ง์ ํตํด editable ์ด true ์ด๋ฉด textarea ๋ฅผ ๋ณด์ฌ์ฃผ์ด ์์ ์ด ๊ฐ๋ฅํ๊ฒ ํ๊ณ , false ์ด๋ฉด ์ผ๋ฐ text ๋ก์จ ๋ณด์ฌ์ง๋๋ก ํ๋ค.
ํด๋น ์ปดํฌ๋ํธ๊ฐ ์๋ ๋ถ๋ถ์ ํด๋ฆญํ์ ๋ editable ์ด false ๊ฐ ๋๋๋ก useRef ์ useEffect ๋ฅผ ์ฌ์ฉํด ์ฃผ์ด ํด๋ฆญ ๋ฐ์์ โhandleClickOutsideโ ํจ์๊ฐ ์คํ๋๋๋ก ํ์๋ค.
๋ฆฌ์กํธ์์๋ DOM ์์๋ฅผ ์ง์ ์ ํํ๋ฉด ์ค๋ฅ๋ฅผ ์ผ์ผํค๊ธฐ ๋๋ฌธ์ useRef ๋ฅผ ํตํด ์ ๊ทผํ๊ณ ์ ํ๋ ์์์ ref ๋ฅผ ๊ฑธ์ด์ ์ ๋ฌํ๋ฉด ref.current ๋ก ํด๋น ์์ ์ ๊ทผ์ด ๊ฐ๋ฅํด์ง๋ค.
ref.current ์๋ ํ์ฌ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด๊ฐ ๋ค์ด๊ฐ๊ฒ ๋๊ณ , ํ์ฌ ๊ฐ์ฒด๊ฐ ์ง๊ธ ํด๋ฆญํ e.target ์ ํฌํจํ๊ณ ์์ง ์์ผ๋ฉด editable ์ false ๋ก ๋ฐ๊พธ์ด (ํด๋ฆญํ ๊ฒ์ด ์ค์ผ์ค๋ฌ ํ์ดํ์ด ์๋๋ผ๋ฉด) edit ๋ชจ๋๊ฐ ๊บผ์ง๊ณ ์ผ๋ฐ ํ ์คํธ๋ก ๋ฐ๋๊ฒ ํ๋ ๊ฒ์ด๋ค.
์ด๋ฏธ์ง ์์ด์ฝ์ ๋๋ฌ ์ค์ผ์ค๋ฌ ํ์ด์ง์ ์ธ๋ค์ผ์ ์ ๋ก๋ ํ๋ ๋ชจ๋ฌ Pop-up ์ฐฝ์ ๋ง๋ค์๋ค.
ํด๋น ์ฌ์ฉ์ ์ ์ฅ์์ ์ข์์ ํน์ ์ซ์ด์๋ ๋ฌด์ ํ์ผ๋ก ๋๋ฅผ ์ ์๋๋ฐ ์ด ๋ถ๋ถ์ ๋ค์ ๊ฐ์ ์ด ํ์ํ ๊ฒ ๊ฐ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ถ๋งํฌ ์ฌ๋ถ๋ฅผ ํตํด ์์ด์ฝ์ด ๋ฐ๋๊ฒ ํ์๋ค.
์๋ gif ์์ ์๋ก๋ฌ๋กํ ๋ค ๊ฐ์ ํด์ ํ๊ทธ ์์๋ค์ด ๋ณด์ด๋๊ฐ?
๊ธฐ์กด์ ์์ด๋์ด๋ ๋ชจ๋ฌ์ฐฝ์์ ํด์ํ๊ทธ๋ฅผ ์ ๋ ฅํ๋ฉด ์ค์ผ์ค๋ฌ ํ์ด์ง์์ ๋ณด์ฌ์ฃผ๊ธฐ๋ก๋ง ํ๊ทธ๋ฅผ ๋์ดํ๊ฒ ํ๋ ค๋ ์๋๋ฅผ ๊ฐ์ง๊ณ ์์๋ค.
ํ์ง๋ง ์ด ๋ํ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ถํธํ ๋ฐฉ์์ด๋ฉด์ ์์ ๋กญ๊ฒ ๋ณ๊ฒฝํ์ง ๋ชปํ๋ค๋ ๋๋์ด ๋ค์๋ค.
ํ๋ก์ ํธ๊ฐ ์ ์ ๊ฐ์ ๋์ด ๊ฐ๋ค!
ํ๋ก ํธ์๋ ํ์ ๋ถ๊ป์ ๋๋ฉ ํ์ด์ง์ Input ์ ํตํด ๊ฒ์์ ํ๋ฉด ์์น ํ์ด์ง๋ก ์ด๋ํ๋ ๋ถ๋ถ์ ๋ํ UI ์ ์ฝ๋ ๊ฐ์ ์ ํด์ฃผ์ จ๋ค.
ํ์ ๋ถ๋ค์ ์์๊ณผ ์กฐ์ธ ๊ทธ๋ฆฌ๊ณ ๋ณธ์ธ์ ๋ ธ๋ ฅ์ผ๋ก ์ ๋ณด๋ค ๋ ๊ฐ์ ๋๊ณ ๋ฐ์ ๋ UI ๋ฅผ ๋ณด์ฌ ์ฃผ์ ์ ๊ธฐ๋ถ์ด ์ข์๋ค.
์ด๋ฅผ ๊ณ๊ธฐ๋ก ์กฐ๊ธ ๋ ์์ ๊ฐ์ ์ฐพ์์ ์ฌ๋ฐ๊ฒ ๋ง๋ค์ด ๋๊ฐ๋ ์ฌ๋ฏธ๋ฅผ ํจ๊ป ๋๊ผ์ผ๋ฉด ์ข๊ฒ ๋ค.
์ค์ ์์ ๊ณผ ์ ์ฅ์ด ๊ฐ๋ฅํ๋๋ก ๋ฐฑ์๋์ ์์ฒญ ์๋ต์ ์ฃผ๊ณ ๋ฐ์์ผ ํ๋ค!
๊ทธ๋ฆฌ๊ณ Redux ์ฝ๋์ ๋ํ ์ดํด๋๋ฅผ ๋ ๋์ฌ๊ฐ๋ ๊ฒ์ด๋ค.
ํ์ดํ ! ๋ ํ ์ ์์ด! ๐๐๐๐๐๐๐๐๐๐